package dream

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"sync"
	"time"
)

type logItem struct {
	id int
	tm time.Time
	s  string
}

const LOG_DEF int = 0
const LOG_KERL int = -417

var pool *sync.Pool
var logs []*logItem
var logsLock *sync.Mutex
var cond *sync.Cond
var logsCh chan int
var logids map[int]string
var logidsLock *sync.Mutex
var wglog sync.WaitGroup

var sPath string
var fmtid bool
var fmtids map[int]*struct{}
var noWrite bool
var noWriteids map[int]*struct{}

func init() {
	noWrite = false
	logs = make([]*logItem, 0, 1024)
	//cond = sync.NewCond(&sync.Mutex{})
	logsLock = &sync.Mutex{}
	logsCh = make(chan int, 1)
	pool = &sync.Pool{
		New: func() interface{} {
			return &logItem{}
		},
	}
	logids = make(map[int]string)
	fmtids = make(map[int]*struct{})
	noWriteids = make(map[int]*struct{})
	logidsLock = &sync.Mutex{}
	sPath = GetCurrentPath()
	sPath += "logs/" + AppName() + "/"
	sPath += time.Now().Format("200601/")
	sPath += time.Now().Format("20060102-150405/")
	wglog = sync.WaitGroup{}
}

type conf struct {
	Off  bool   `json:"off"`
	Offs []int  `json:"offs"`
	Path string `json:"path"`
	Fmt  bool   `json:"fmt"`
	Fmts []int  `json:"fmts"`
}

func LogStartup(config string) {
	fmt.Println("LogStartup")
	if len(config) > 0 {
		_conf := conf{}
		err := json.Unmarshal([]byte(config), &_conf)
		if err == nil {
			if _conf.Fmt {
				fmtid = true
			} else {
				for i := 0; i < len(_conf.Fmts); i++ {
					fmtids[_conf.Fmts[i]] = nil
				}
			}
			if len(_conf.Path) > 0 {
				_conf.Path, _ = PathCheckEndChar(_conf.Path, true)
				if IsFullPath(_conf.Path) {
					sPath = _conf.Path
					sPath += AppName() + "/"
					sPath += time.Now().Format("200601/")
					sPath += time.Now().Format("20060102-150405/")
				} else {
					sPath = workPath
					sPath += _conf.Path
					sPath += AppName() + "/"
					sPath += time.Now().Format("200601/")
					sPath += time.Now().Format("20060102-150405/")

				}
			}
			if _conf.Off {
				noWrite = true
			} else {
				for i := 0; i < len(_conf.Offs); i++ {
					noWriteids[_conf.Offs[i]] = nil
				}
			}
		}
	}
	if !IsFileIsExist(sPath) {
		os.MkdirAll(sPath, os.ModePerm)
	}
	addId(LOG_DEF, "log")
	addId(LOG_KERL, "kerl")
	wglog.Add(1)
	go selfthread()
	//logsCh <- 0
}
func addId(id int, file string) {
	sss := sPath
	sss += file
	//CreatePath(sss.c_str())
	logidsLock.Lock()
	logids[id] = sss
	logidsLock.Unlock()
}
func LogAdd(id int, file string) {
	if id != 0 {
		addId(id, file)
	}
}
func Log(a ...interface{}) {
	Logi(0, a...)
}
func Logf(f string, a ...interface{}) {
	Logfi(0, f, a...)
}

func Logi(id int, a ...interface{}) {
	sss := fmt.Sprint(a...)
	if fmtid {
		fmt.Println(sss)
	} else {
		if _, ok := fmtids[id]; ok {
			fmt.Println(sss)
		}
	}
	if noWrite {
		return
	}
	if _, ok := noWriteids[id]; ok {
		return
	}
	it := pool.Get().(*logItem)
	it.id = id
	it.s = sss
	//cond.L.Lock()
	logsLock.Lock()
	it.tm = time.Now()
	logs = append(logs, it)
	//cond.L.Unlock()
	logsLock.Unlock()
	//cond.Signal()
	select {
	case logsCh <- 0:
		//fmt.Println("logs tick", n)
		return
	default:
		//fmt.Println("logs untick", n)
		return
	}
}

func Logfi(id int, f string, a ...interface{}) {
	sss := fmt.Sprintf(f, a...)
	if fmtid {
		fmt.Println(sss)
	} else {
		if _, ok := fmtids[id]; ok {
			fmt.Println(sss)
		}
	}
	if noWrite {
		return
	}
	if _, ok := noWriteids[id]; ok {
		return
	}
	it := pool.Get().(*logItem)
	it.id = id
	it.s = sss
	//cond.L.Lock()
	logsLock.Lock()
	it.tm = time.Now()
	logs = append(logs, it)
	//cond.L.Unlock()
	logsLock.Unlock()
	//cond.Signal()
	select {
	case logsCh <- 0:
		//fmt.Println("logs tick", n)
		return
	default:
		//fmt.Println("logs untick", n)
		return
	}
}

type fileState struct {
	lasttime time.Time
	cursize  int
	curfile  string
}

func LogShutdown() {
	logsLock.Lock()
	logs = append(logs, nil)
	logsLock.Unlock()
	select {
	case logsCh <- 0:
		break
	default:
		break
	}
	time.Sleep(time.Duration(1) * time.Second)
	wglog.Wait()
}

func isSameHour(t1, t2 *time.Time) bool {
	if t1.Year() != t2.Year() || t1.Month() != t2.Month() || t1.Day() != t2.Day() || t1.Hour() != t2.Hour() {
		return false
	}
	return true
}

func selfthread() {
	defer func() { CrashSnap(recover()) }()
	filestate := make(map[int]*fileState)
	logstemp := make([]*logItem, cap(logs))
	exit := false
	for !exit {
		/*cond.L.Lock()
		cond.Wait()
		logstempcount := len(logs)
		if logstempcount > 0 {
			if len(logstemp) < logstempcount {
				logstemp = make([]*logItem, logstempcount)
			}
			copy(logstemp, logs)
			logs = logs[0:0]
		}
		cond.L.Unlock()*/
		<-logsCh
		logsLock.Lock()
		logstempcount := len(logs)
		if logstempcount > 0 {
			if len(logstemp) < logstempcount {
				logstemp = make([]*logItem, logstempcount)
			}
			copy(logstemp, logs)
			logstemp = logstemp[0:logstempcount]
			logs = logs[0:0]
		}
		logsLock.Unlock()

		if logstempcount > 0 {
			filebf := make(map[int][]*logItem)
			for i := 0; i < logstempcount; i++ {
				if logstemp[i] != nil {
					plogitem, ok := filebf[logstemp[i].id]
					if !ok {
						filebf[logstemp[i].id] = []*logItem{logstemp[i]}
					} else {
						filebf[logstemp[i].id] = append(plogitem, logstemp[i])
					}
				} else {
					exit = true
				}
			}

			for k, v := range filebf {
				logidsLock.Lock()
				file, fileok := logids[k]
				logidsLock.Unlock()

				if !fileok {
					fmt.Println(" EEEEEEEEEEERR Log Id: ", k)
				} else {
					file_state, file_state_ok := filestate[k]
					if !file_state_ok {
						file_state = &fileState{}
						filestate[k] = file_state
					}

					var f *os.File
					var err error
					if len(file_state.curfile) == 0 || file_state.cursize > 10*1024*1024 {
						file_state.curfile = file + time.Now().Format(".2006_01_02.15_04_05") + ".txt"
						file_state.cursize = 0
						f, err = os.Create(file_state.curfile)
					} else {
						if IsFileIsExist(file_state.curfile) {
							f, err = os.OpenFile(file_state.curfile, os.O_APPEND, 0666)
						} else {
							f, err = os.Create(file_state.curfile)
						}
					}

					mode_addtime := true

					if err == nil {
						w := bufio.NewWriter(f) //创建新的 Writer 对象
						var writed int
						for i := 0; i < len(v); i++ {
							if mode_addtime {
								var tms string
								if !isSameHour(&(file_state.lasttime), &(v[i].tm)) {
									tms = v[i].tm.Format("2006-01-02 15:04:05 ")
									file_state.lasttime = v[i].tm
								} else {
									tms = v[i].tm.Format(" 15:04:05 ")
								}
								writed, err = w.WriteString(tms)
								file_state.cursize += writed
							}
							writed, err = w.WriteString(v[i].s)
							file_state.cursize += writed
							if mode_addtime {
								writed, err = w.WriteString("\r\n")
								file_state.cursize += writed
							}
						}
						w.Flush()
						f.Close()
					}
				}
			}

			for i := 0; i < logstempcount; i++ {
				pool.Put(logstemp[i])
			}
		}

		time.Sleep(1)
	}
	wglog.Done()
	fmt.Println(" ...Log stop")
}
